﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Parser
{
    public class PersonAttribute:Attribute
    {
        public string Name { get; set; }
        public string ForPropery { get; set; }
        public bool IsFake { get; set; }
    }

    public enum Gender
    {
        Male,
        Female,
        Undefined
    }
    public enum Sites
    {
        Odnoklassniki,
        MoiKrug,
        Work,
        Home,
        LinkedIn
    }
    public enum Dates
    {
        Birthday
    }


    public class DescriptionImage
    {
        public Image Image { get; set; }
        public string Description { get; set; }
    }

    public class NetworkPerson
    {
        private const string MainDelimiter = "|";
        private const string InternalDelimiter = "::";

        [PersonAttribute(Name = "ShowName")]
        public string ShowName { get; set; }
        [PersonAttribute(Name="Name")]
        public string Name { get; set; }
        [PersonAttribute(Name="Surname")]
        public string SurName { get; set; }
        [PersonAttribute(Name="MiddleName")]
        public string MiddleName { get; set; }
        [PersonAttribute(Name = "MaidenName")]
        public string MaidenName { get; set; }
        public Image MainPicture { get; set; }
        public Dictionary<string, List<DescriptionImage>> Pictures { get; set; }
        [PersonAttribute(Name = "DateTypes", IsFake = true, ForPropery = "Dates")]
        public string DateTypes { get; set; }
        [PersonAttribute(Name="Dates")]
        public Dictionary<Dates, DateTime> Dates { get; set; }
        [PersonAttribute(Name = "SiteTypes", IsFake = true, ForPropery = "Sites")]
        public string SiteTypes { get; set; }
        [PersonAttribute(Name="Sites")]
        public Dictionary<Sites, string> Sites { get; set; }
        [PersonAttribute(Name="Location")]
        public string Location { get; set; }
        [PersonAttribute(Name="Gender")]
        public Gender Gender { get; set; }
        public DateTime? Birthday
        {
            get
            {
                if (Dates.ContainsKey(Parser.Dates.Birthday))
                    return Dates[Parser.Dates.Birthday];
                return null;
            }
            set
            {
                if (!value.HasValue && Dates.ContainsKey(Parser.Dates.Birthday))
                {
                    Dates.Remove(Parser.Dates.Birthday);
                    return;
                }
                Dates[Parser.Dates.Birthday] = value.Value;
            }
        }
        public string OdnoklassnikiSite
        {
            get
            {
                return Sites.ContainsKey(Parser.Sites.Odnoklassniki) ? Sites[Parser.Sites.Odnoklassniki] : null;
            }
            set
            {
                if (string.IsNullOrWhiteSpace(value) && Sites.ContainsKey(Parser.Sites.Odnoklassniki))
                {
                    Sites.Remove(Parser.Sites.Odnoklassniki);
                    return;
                }
                Sites[Parser.Sites.Odnoklassniki] = value;
            }
        }

        public NetworkPerson()
        {
            Gender = Gender.Undefined;
            Dates = new Dictionary<Dates, DateTime>();
            Sites = new Dictionary<Sites, string>();
            Pictures = new Dictionary<string, List<DescriptionImage>>();
        }

        public void SavePerson(string path)
        {
            if (string.IsNullOrWhiteSpace(FullName))
                return;
            if (!path.EndsWith("\\"))
                path += "\\";
            path += FullName;
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
            using (var sr = new StreamWriter(path+"\\Info.txt", false, Encoding.Default))
            {
                sr.Write(PersonInfo);
                sr.Flush();
            }
            if (MainPicture != null)
                MainPicture.Save(path+"\\main.jpg");
            if (Pictures != null && Pictures.Count > 0)
                foreach (var picture in Pictures)
                {
                    if (picture.Value.Count == 0)
                        continue;
                    foreach (var descriptionImage in picture.Value)
                        descriptionImage.Image.Save(GetFileName(path + "\\" + CorrectFileName(picture.Key), descriptionImage.Description));
                }
        }
        public void DeletePersonsPicture(string path)
        {
            if (string.IsNullOrWhiteSpace(FullName))
                return;
            if (!path.EndsWith("\\"))
                path += "\\";
            if (Directory.Exists(path + CorrectFileName(FullName)))
                Directory.Delete(path + CorrectFileName(FullName),true);
        }
        public void ChangePersonsPicture(string path, string newFullName)
        {
            if (string.IsNullOrWhiteSpace(FullName))
                return;
            if (!path.EndsWith("\\"))
                path += "\\";
            if (Directory.Exists(path + CorrectFileName(FullName)) && CorrectFileName(FullName).ToLower() != CorrectFileName(newFullName).ToLower())
                Directory.Move(path + CorrectFileName(FullName), path + CorrectFileName(newFullName));
        }

        public void SavePersonToList(string path)
        {
            if (string.IsNullOrWhiteSpace(FullName))
                return;
            if (!path.EndsWith("\\"))
                path += "\\";
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            if (!File.Exists(path + "\\List.csv"))
            {
                using (var sr = new StreamWriter(path + "\\List.csv", false, Encoding.Default))
                {
                    sr.WriteLine(GetHead());
                    sr.Flush();
                }
            }

            using (var sr = new StreamWriter(path + "\\List.csv", true, Encoding.Default))
            {
                sr.WriteLine(GetSimpleString());
                sr.Flush();
            }
            if (!Directory.Exists(path + CorrectFileName(FullName)))
                Directory.CreateDirectory(path + CorrectFileName(FullName));
            if (MainPicture != null)
                MainPicture.Save(path + CorrectFileName(FullName)+"\\main.jpg");
            if (Pictures != null && Pictures.Count > 0)
                foreach (var picture in Pictures.Where(picture => picture.Value.Count != 0))
                {
                    if (!Directory.Exists(path + CorrectFileName(FullName) +"\\"+ CorrectFileName(picture.Key)))
                        Directory.CreateDirectory(path + CorrectFileName(FullName) +"\\" + CorrectFileName(picture.Key));
                    foreach (var descriptionImage in
                        picture.Value.Where(descriptionImage => descriptionImage.Image != null))
                        try
                        {
                            descriptionImage.Image.Save(GetFileName(path + CorrectFileName(FullName) + "\\" + CorrectFileName(picture.Key),
                                                                    descriptionImage.Description));
                        }
                        catch
                        {
                        }
                }
        }

        public string PersonInfo
        {
            get
            {
                var res = new StringBuilder();
                if (!string.IsNullOrWhiteSpace(FullName))
                    res.AppendLine(string.Format("Full name: {0}", FullName));
                if (Gender != Gender.Undefined)
                    res.AppendLine(string.Format("Gender: {0}", Gender));
                if (!string.IsNullOrWhiteSpace(Location))
                    res.AppendLine(string.Format("Location: {0}", Location));

                foreach (var dateTime in Dates)
                    res.AppendLine(string.Format("{0}: {1:yyyy-d-M}", dateTime.Key, dateTime.Value));

                foreach (var site in Sites)
                    res.AppendLine(string.Format("{0} url: {1}", site.Key, site.Value));

                return res.ToString();
            }
        }

        public string FullName
        {
            get
            {
                var res = string.Format("{0} ({1}) {2} {3}", SurName, MaidenName, Name, MiddleName).Replace("()", " ").Trim();
                if (res.StartsWith("("))
                    res = res.Replace("(", "").Replace(")", "");
                while (res.Contains("  "))
                    res = res.Replace("  ", " ");
                return res;
            }
        }

        public string GetSimpleString()
        {
            var fields = typeof(NetworkPerson).GetProperties();
            var res = new StringBuilder();
            foreach (var fieldInfo in from fieldInfo in fields
                                      let at = fieldInfo.GetCustomAttributes(typeof (PersonAttribute), true).FirstOrDefault() as PersonAttribute
                                      where at != null && !at.IsFake
                                      select fieldInfo)
            {
                res.Append(res.Length > 0 ? MainDelimiter : string.Empty);
                res.Append(GetPropertyString(fieldInfo));
            }
            return res.ToString();
        }

        protected void SetProperty(PropertyInfo fieldInfo, string value, string parameters = null)
        {
            if (fieldInfo ==null || string.IsNullOrWhiteSpace(value))
                return;
            if (fieldInfo.PropertyType == typeof(string))
            {
                fieldInfo.SetValue(this, value, null);
                return;
            }
            if (fieldInfo.PropertyType == typeof(Gender))
            {
                var gnd = Gender.Undefined;
                Enum.TryParse(value, true, out gnd);
                fieldInfo.SetValue(this, gnd, null);
                return;
            }
            if (fieldInfo.PropertyType == typeof(Dictionary<Dates, DateTime>))
            {
                if (string.IsNullOrWhiteSpace(parameters))
                    return;
                var dts = value.Split(new[] {InternalDelimiter}, StringSplitOptions.None);
                var tps = parameters.Split(new[] { InternalDelimiter }, StringSplitOptions.None);
                if (dts.Length != tps.Length)
                    return;
                Dates = new Dictionary<Dates, DateTime>();
                foreach (var tp in tps)
                {
                    var t = Parser.Dates.Birthday;
                    Enum.TryParse(tp, true, out t);
                    var s = dts[Dates.Count];
                    var enUS = new CultureInfo("en-US");
                    var dt = DateTime.Now;
                    DateTime.TryParseExact(s, "yyyy-M-d", enUS, DateTimeStyles.None, out dt);
                    Dates.Add(t, dt);
                }

                return;
            }
            if (fieldInfo.PropertyType == typeof(Dictionary<Sites, string>))
            {
                if (string.IsNullOrWhiteSpace(parameters))
                    return;
                var sts = value.Split(new[] { InternalDelimiter }, StringSplitOptions.None);
                var tps = parameters.Split(new[] { InternalDelimiter }, StringSplitOptions.None);
                if (sts.Length != tps.Length)
                    return;
                Sites = new Dictionary<Sites, string>();
                foreach (var tp in tps)
                {
                    var t = Parser.Sites.Home;
                    Enum.TryParse(tp, true, out t);
                    var s = sts[Sites.Count];
                    Sites.Add(t, s);
                }

                return;
            }
            return;
        }

        private string GetPropertyString(PropertyInfo fieldInfo)
        {
            if (fieldInfo.PropertyType == typeof(string))
                return fieldInfo.GetValue(this,null) as string;
            if (fieldInfo.PropertyType == typeof(Gender))
                return ((Gender)(fieldInfo.GetValue(this,null))).ToString();
            if (fieldInfo.PropertyType == typeof(Dictionary<Dates, DateTime>))
            {
                var ob = fieldInfo.GetValue(this,null) as Dictionary<Dates, DateTime>;
                if (ob == null)
                    return MainDelimiter;
                var dtk = new StringBuilder();
                var dtv = new StringBuilder();
                foreach (var dateTime in ob)
                {
                    dtk.Append(dtk.Length > 0 ? InternalDelimiter : string.Empty);
                    dtk.Append(dateTime.Key);
                    dtv.Append(dtv.Length > 0 ? InternalDelimiter : string.Empty);
                    dtv.Append(dateTime.Value.ToString("yyyy-M-d"));
                }
                return dtk + MainDelimiter + dtv;
            }
            if (fieldInfo.PropertyType == typeof(Dictionary<Sites, string>))
            {
                var ob = fieldInfo.GetValue(this,null) as Dictionary<Sites, string>;
                if (ob == null)
                    return MainDelimiter;
                var dtk = new StringBuilder();
                var dtv = new StringBuilder();
                foreach (var dateTime in ob)
                {
                    dtk.Append(dtk.Length > 0 ? InternalDelimiter : string.Empty);
                    dtk.Append(dateTime.Key);
                    dtv.Append(dtv.Length > 0 ? InternalDelimiter : string.Empty);
                    dtv.Append(dateTime.Value);
                }
                return dtk + MainDelimiter + dtv;
            }
            return string.Empty;
        }

        public static string GetHead()
        {
            var fields = typeof(NetworkPerson).GetProperties();
            var res = new StringBuilder();
            foreach (var at in
                fields.Select(fieldInfo => fieldInfo.GetCustomAttributes(typeof (PersonAttribute), true).FirstOrDefault()).OfType<PersonAttribute>())
            {
                res.Append(res.Length > 0 ? MainDelimiter:string.Empty);
                res.Append(at.Name);
            }
            return res.ToString();
        }

        public static NetworkPerson[] GetPersons(string path)
        {
            if (!path.EndsWith("\\"))
                path += "\\";
            if (!Directory.Exists(path))
                return new NetworkPerson[0];

            if (!File.Exists(path + "\\List.csv"))
                return new NetworkPerson[0];
            var res = new List<NetworkPerson>();
            using (var sr = new StreamReader(path + "\\List.csv", Encoding.Default))
            {
                var begin = true;
                var dct = new Dictionary<PropertyInfo, int>();
                var properties =
                            typeof(NetworkPerson).GetProperties().Where(
                                it =>
                                    it.GetCustomAttributes(typeof(PersonAttribute), true).FirstOrDefault() != null &&
                                it.GetCustomAttributes(typeof(PersonAttribute), true).FirstOrDefault() is
                                PersonAttribute);
                while (!sr.EndOfStream)
                {
                    var ln = sr.ReadLine();
                    if (begin)
                    {
                        var itm = ln.Split(new[] { MainDelimiter }, StringSplitOptions.None);
                        var cnt = -1;
                        foreach (var s in itm)
                        {
                            cnt++;
                            var p =
                                properties.FirstOrDefault(
                                    it =>
                                    ((PersonAttribute) it.GetCustomAttributes(typeof (PersonAttribute), true).FirstOrDefault()).Name.Trim().ToLower() == s.Trim().ToLower());
                            if (p != null)
                                dct.Add(p, cnt);
                        }
                        begin = false;
                        continue;
                    }
                    var person = new NetworkPerson();
                    var itms = ln.Split(new[] { MainDelimiter }, StringSplitOptions.None);
                    foreach (var propertyInfo in properties)
                    {
                        if (dct[propertyInfo] >= itms.Length)
                            continue;
                        var at =
                            (PersonAttribute)
                            propertyInfo.GetCustomAttributes(typeof (PersonAttribute), true).FirstOrDefault();
                        if (at.IsFake)
                            continue;
                        var pr = properties.FirstOrDefault(it => ((PersonAttribute)
                                                         it.GetCustomAttributes(typeof (PersonAttribute), true).
                                                             FirstOrDefault()).IsFake &&
                                                        !string.IsNullOrWhiteSpace(((PersonAttribute)
                                                                                    it.GetCustomAttributes(
                                                                                        typeof (PersonAttribute), true).
                                                                                        FirstOrDefault()).ForPropery) &&
                                                        ((PersonAttribute)
                                                         it.GetCustomAttributes(typeof (PersonAttribute), true).
                                                             FirstOrDefault()).ForPropery.Trim().ToLower() ==
                                                        propertyInfo.Name.Trim().ToLower());
                        if (pr != null && dct[pr] >= itms.Length)
                            pr = null;
                        person.SetProperty(propertyInfo, itms[dct[propertyInfo]], pr != null?itms[dct[pr]]:null);
                    }
                    res.Add(person);
                }
            }
            return res.ToArray();
        }

        public static void SavePersons(string path, NetworkPerson[] persons)
        {
            if (!path.EndsWith("\\"))
                path += "\\";
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            if (File.Exists(path + "\\List.csv"))
                File.Delete(path + "\\List.csv");

            using (var sr = new StreamWriter(path + "\\List.csv", false, Encoding.Default))
            {
                sr.WriteLine(GetHead());
                sr.Flush();
            }
            if (persons == null || persons.Length == 0)
                return;
            using (var sr = new StreamWriter(path + "\\List.csv", true, Encoding.Default))
            {
                foreach (var networkPerson in persons)
                    sr.WriteLine(networkPerson.GetSimpleString());
                sr.Flush();
            }
        }

        public static string[] GetNames(string path)
        {
            if (!path.EndsWith("\\"))
                path += "\\";
            if (!Directory.Exists(path))
                return new string[0];

            if (!File.Exists(path + "\\List.csv"))
                return new string[0];
            var res = new List<string>();
            using (var sr = new StreamReader(path + "\\List.csv", Encoding.Default))
            {
                var begin = true;
                var idx = -1;
                while (!sr.EndOfStream)
                {
                    var ln = sr.ReadLine();
                    if (begin)
                    {
                        var itm = ln.Split(new[] { MainDelimiter}, StringSplitOptions.None);
                        foreach (var s in itm)
                        {
                            idx++;
                            if (s == "ShowName")
                                break;
                        }
                        if (idx == itm.Length)
                            return new string[0];
                        begin = false;
                        continue;
                    }
                    var itms = ln.Split(new[] {MainDelimiter}, StringSplitOptions.None);
                    if (itms.Length <= idx)
                        continue;
                    res.Add(itms[idx]);
                }
            }
            return res.ToArray();
        }

        public static NetworkPerson GetFakePerson()
        {
            var p = new NetworkPerson();
            p.Dates.Add(Parser.Dates.Birthday, new DateTime(1983, 10, 21));
            p.Gender = Gender.Male;
            p.Location = "Minsk, Belarus";
            p.MaidenName = "Девичья";
            p.MainPicture = new Bitmap(10, 10);
            p.MiddleName = "Отчество";
            p.Name = "Имя";
            //p.Pictures.Add(new Bitmap(5,5));
            p.Sites.Add(Parser.Sites.Odnoklassniki,"http://www.odnoklassniki.ru/getFakeName");
            p.Sites.Add(Parser.Sites.LinkedIn, "http://www.linkedin.com/getFakeName");
            p.SurName = "Фамилия";
            return p;
        }

        private static string GetFileName(string path, string name, string extension="jpg")
        {
            var cnt = 0;
            var cp = CompilePath(path, name, extension, cnt);
            while (File.Exists(cp))
            {
                cnt++;
                if (cnt > 1000)
                    return string.Empty;
                cp = CompilePath(path, name, extension, cnt);
            }
            return cp;
        }

        private static string CompilePath(string path, string name, string extension, int number)
        {
            if (string.IsNullOrWhiteSpace(path))
                path = "C:\\";
            if (!path.EndsWith("\\"))
                path += "\\";
            if (string.IsNullOrWhiteSpace(extension))
                extension = "jpg";
            if (number < 0 || number >= 1000)
                number = 0;
            name = CorrectFileName(name);
            return string.IsNullOrWhiteSpace(name) ?
                       number > 0
                             ? string.Format("{0}{1:000}.{2}", path, number, extension)
                             : string.Format("{0}001.{1}", path, extension):
                       number > 0
                             ? string.Format("{0}{1}_{2:000}.{3}", path, name, number, extension)
                             : string.Format("{0}{1}.{2}", path, name, extension);
        }

        private static string CorrectFileName(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
                return string.Empty;
            name =
                name.Replace('$', ' ').Replace('!', ' ').Replace('@', ' ').Replace('\\', ' ').Replace('/', ' ').Replace(
                    '|', ' ').Replace('*', ' ').Replace('\'', ' ').Replace('"', ' ').Replace(':', ' ').Replace(';', ' ').Replace('?', ' ')
                    .Replace(',', ' ').Replace('.', ' ');
            while (name.IndexOf("  ") > -1)
                name = name.Replace("  ", " ");
            name = name.Trim();
            if (name.Length > 245)
                name = name.Remove(246);
            return name;

        }
    }
}
